package com.ymt.bpm.engine.bpmn;

import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import com.ymt.bpm.engine.EngineConst;
import com.ymt.bpm.util.w3cdom.DataRow;
import com.ymt.bpm.util.w3cdom.NumberScaleAttrFilter;
import com.ymt.bpm.util.w3cdom.XmlDomUtil;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSONObject;


@SuppressWarnings("deprecation")
public class BpmnRender {
	
	/**
	 * Logger.
	 */
	private static Logger logger = Logger.getLogger(BpmnRender.class.getName());

	private static final String clsNameBpmTaskListenr = com.ymt.bpm.engine.listener.activity.UserTaskDelegateListener.class.getName();
	private static final String clsNameProcessExecutionListener = com.ymt.bpm.engine.listener.ProcessExecutionListener.class.getName();
	private static final String clsNameServiceTaskDelegate = com.ymt.bpm.engine.listener.activity.AsyncServiceTaskDelegateListener.class.getName();

	/**
	 * 生成 bpmn标准文件的初始模版 xml.
	 */
	private String targetBpmnStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+
			"<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
			        + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:activiti=\"http://activiti.org/bpmn\" "
			        + "xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" "
			        + "xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:bpmn2=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" "
			        + "xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\" "
			        + "typeLanguage=\"http://www.w3.org/2001/XMLSchema\" expressionLanguage=\"http://www.w3.org/1999/XPath\" "
			        + "targetNamespace=\"http://bpmn.io/schema/bpmn\" id=\"bpmn-diagram\" "
			        + "xsi:schemaLocation=\"http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd\">"+
			"  <collaboration id=\"Collaboration_1\">"+
			"    <participant id=\"pool1\" name=\"ProcessPool1\" processRef=\"process_pool1\"></participant>"+
			"  </collaboration>"+
			"  <process id=\"process_pool1\" name=\"process_pool1\" isExecutable=\"true\">"+
			"  </process>"+
			"  <bpmndi:BPMNDiagram id=\"BPMNDiagram_Collaboration\">"+
			"    <bpmndi:BPMNPlane bpmnElement=\"Collaboration_1\" id=\"BPMNPlane_Collaboration_1\">"+
			"    </bpmndi:BPMNPlane>"+
			"  </bpmndi:BPMNDiagram>"+
			"</definitions>";
	
	/**
	 * 在部署之前需要去掉的节点类型
	 */
	private String[] omitNodes = {"bpmn2:mileStoneGroup",//阶段
			"bpmn2:participantGroup",//泳道分组
			"bpmn2:textAnnotation",//文本注释
			"bpmn2:association",//关联线
			"bpmn2:groupMarker"//框型标记
			};
	
	/**
	 * 流程定义ID
	 */
	private String processId;
	/**
	 * 流程定义名称
	 */
	private String processName;
	/**
	 * 正在部署的下一版本ID
	 */
	private int deployingVersion;
	/**
	 * 流程设计器设计的原始bpmn文件.
	 */
	private String sourceXmlStr;
	//private Document sourceDoc;
	/**
	 * 转换成的新的bpmn document
	 */
	private Document targetDoc;
	/**
	 * LocalName别名
	 */
	private Map<String, String> nameAlias;
	/**
	 * 前缀别名
	 */
	private Map<String, String> prefixAlias;
	/**
	 * 包含所有流程组件的所有属性的配置信息.
	 */
	private JSONObject bpmnConfig;
	/**
	 * 记录所有的合并节点，非网关
	 * key为节点id，值为节点的来源路径id列表
	 */
	private Map<String, List<String>> joinActivities;
	/**
	 * 记录所有EventDefinition用于添加到definitions根节点
	 */
	private List<DataRow> firstLvlElems = new ArrayList<DataRow>();
	
	/*static {
		String abpath = BpmnRender.class.getClassLoader().getResource(".").getPath();
		logger.finer(abpath);
	}*/
	
	/**
	 * Constructor.
	 * @param processId 流程定义ID
	 * @param processName 流程定义名称
	 * @param sourceXmlStr 原始xml string
	 * @param targetXmlTemplateStr 生成 bpmn标准文件的初始模板 xml
	 * @param bpmnConfigStr 包含所有流程组件的所有属性的配置信息, string格式
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws XPathExpressionException
	 */
	public BpmnRender(String processId, String processName, int deployingVersion,
			String sourceXmlStr, String targetXmlTemplateStr,
			String bpmnConfigStr) throws ParserConfigurationException,
			SAXException, IOException, XPathExpressionException {
		this.sourceXmlStr = sourceXmlStr;
		//sourceDoc = XmlDomUtil.buildXmlFromString(sourceXmlStr);
		targetDoc = XmlDomUtil.buildXmlFromString(targetBpmnStr);
		
		//如果未指定模版，则使用默认模板
		if (targetXmlTemplateStr==null || targetXmlTemplateStr.length()==0) {
			this.targetBpmnStr = targetXmlTemplateStr;
		}
		
		this.processId = processId;
		this.processName = processName;
		this.deployingVersion = deployingVersion;
		this.bpmnConfig = JSONObject.parseObject(bpmnConfigStr);
		this.joinActivities = new HashMap<String, List<String>>();
		
		//初始化xml namespace别名
		initAlias();
		
		logger.finest("source xml=\n"+sourceXmlStr);
		//转换
		transform();
	}
	
	/**
	 * 输出转化结果bpmn文档.
	 * @return
	 */
	public Document getTargetDocument() {
		return targetDoc;
	}
	
	/**
	 * 输出转化结果bpmn文档为string.
	 * @return
	 * @throws TransformerException
	 */
	public String getTargetDocumentString() throws TransformerException {
		return XmlDomUtil.transformXmlToString(targetDoc);
	}
	
	/**
	 * 格式化输出xml到指定文件.
	 * @param absolutePath
	 * @throws IOException
	 * @throws TransformerException
	 */
	/*public void toFile(String absolutePath) throws IOException, TransformerException {
		//FileUtils.writeStringToFile(new File(absolutePath), getTargetDocumentString(), Const.ENCODING, false);
		
    	OutputFormat format = new OutputFormat(targetDoc);  
        format.setLineWidth(65);  
        format.setIndenting(true);  
        format.setIndent(4);  
        StringWriter out = new StringWriter();  
        XMLSerializer serializer = new XMLSerializer(out, format);  
        serializer.serialize(targetDoc);
        FileUtils.writeStringToFile(new File(absolutePath), out.toString(), Const.ENCODING, false);
	}*/
    
	/**
	 * 转换原始bpmn文件到可部署的bpmn文件.
	 * @throws XPathExpressionException
	 * @throws IOException
	 */
    private void transform() throws XPathExpressionException, IOException {
    	
		Node processNode = targetDoc.getElementsByTagName("process").item(0);
		
		//加入流程发起的ExecutionListener
	    DataRow exeLsnr = new DataRow("activiti:executionListener");
	    exeLsnr.addAttribute("class", clsNameProcessExecutionListener);
	    exeLsnr.addAttribute("event", "start");
	    DataRow exeLsnr1 = new DataRow("activiti:executionListener");
	    exeLsnr1.addAttribute("class", clsNameProcessExecutionListener);
	    exeLsnr1.addAttribute("event", "end");
	    DataRow extElem = new DataRow("extensionElements");
	    extElem.addChildren(exeLsnr);
	    extElem.addChildren(exeLsnr1);
	    XmlDomUtil.addDataRow(targetDoc, processNode, extElem);
		
		//set process id 修改流程定义id为bpdId
		for (int i=0; i<processNode.getAttributes().getLength(); i++) {
			Node attr = processNode.getAttributes().item(i);
			if ("id".equalsIgnoreCase(attr.getNodeName())) {
				attr.setNodeValue(processId);
			} else if ("name".equalsIgnoreCase(attr.getNodeName())) {
				attr.setNodeValue(processName);
			}
		}
		Node participantNode = targetDoc.getElementsByTagName("participant").item(0);
		for (int i=0; i<participantNode.getAttributes().getLength(); i++) {
			Node attr = participantNode.getAttributes().item(i);
			if ("processRef".equalsIgnoreCase(attr.getNodeName())) {
				attr.setNodeValue(processId);
			}
		}
		
		//创建EventListener
		/*DataRow lsnExtensionElements = new DataRow("extensionElements")
			.addChildren(new DataRow("extensionElements")
				.addChildren(new DataRow("activiti:eventListener")
					.addAttribute("class", CommonEventListener.class.getName())));*/
		
		//创建laneSet
		DataRow laneSet = new DataRow("laneSet");
		laneSet.addAttribute("id", "laneSet_process_pool1");
		List<DataRow> lanes = getDataRowByXPath("//bpmn2:lane", true);
		//DataRow.removeAttr(lanes, "isExecutable");
		laneSet.setChildren(lanes);
		laneSet.removeChildrenByLocalName("childLaneSet", true);
		
		//nodes reference on lanes
		/*for (DataRow lane:lanes) {
			List<DataRow> lanenodes = getDataRowByXPath(
					"//bpmn2:process[@id='"
							+ lane.getAttributes().get("processRef")
							+ "']/*[name(.) != 'bpmn2:sequenceFlow'][name(.) != 'bpmn2:mileStone'][name(.) != 'bpmn2:textAnnotation']",
					false);
			//logger.finer("=============-=============="+lane.getAttributes().get("id"));
			lane.removeAttr("processRef", false);	//因为lane不需要，去掉原participant上的属性processRef
			for (DataRow node:lanenodes) {
				lane.addElement("flowNodeRef", node.getAttributes().get("id"));
			}
		}*/
		
		//将laneSet加入process
		XmlDomUtil.addDataRow(targetDoc, 
				processNode, 
				laneSet);
		
		//nodes on lane 解析泳道上的节点
		List<DataRow> nodesOnLane = getDataRowByXPath("//bpmn2:process/*", false, this.omitNodes);
		if (nodesOnLane != null && nodesOnLane.size()>0) {
			for (DataRow ut : nodesOnLane) {
				String nodeId = ut.getAttribute("id");
				logger.finest("nodesOnLane === "+ut.getLocalName()+"["+nodeId+"]");
				if ("userTask".equals(ut.getLocalName())) {	//任务
					ut.addChildren(new DataRow("extensionElements")
							.addChildren(new DataRow("activiti:taskListener")
									.addAttribute("event", "create")
									.addAttribute("class",
											clsNameBpmTaskListenr)));
					//增加路由信息 default transitioin
					JSONObject elemConf = bpmnConfig.getJSONObject(nodeId);
					if (elemConf.getJSONObject("route") != null) {
						String defaultPath = elemConf.getJSONObject("route").getString("defaultTransition");
						if (defaultPath != null && defaultPath.length()>0) {
							ut.addAttribute("default", defaultPath);
						}
					}
					//检查是否是合并节点并记录
					checkIsJoinActivity(nodeId);
				} else if ("sequenceFlow".equals(ut.getLocalName())) {	//连线 sequenceFlow
					calculateFlowCondtion(ut, nodeId);
				} else if ("subProcess".equals(ut.getLocalName())) {	//子流程
					if ("SubProcessCollapsed".equalsIgnoreCase(bpmnConfig.getJSONObject(nodeId).getString("i18ntype"))) {	//嵌套子流程
						subprocessToCallActivity(ut, nodeId);
						checkIsJoinActivity(nodeId);
					} else if("SubProcess".equalsIgnoreCase(bpmnConfig.getJSONObject(nodeId).getString("i18ntype"))){//一般内嵌子流程
						
						//去掉不必要的属性
						ut.removeAttr("fold", false);
						ut.removeAttr("foldWidth", false);
						ut.removeAttr("foldHeight", false);
						ut.removeAttr("unfoldWidth", false);
						ut.removeAttr("unfoldHeight", false);
						
//						NodeList nl = getNodesByXPath("//bpmn2:subProcess[@id='"+nodeId+"']/*[name(.) != 'bpmn2:textAnnotation']");
						NodeList nl = getNodesByXPath("//bpmn2:subProcess[@id='"+nodeId+"']");
						List<String> exc = Arrays.asList(this.omitNodes);
						
						for (int i=0; i<nl.getLength(); i++) {
							Node node = nl.item(i);
//							if("subProcess".equalsIgnoreCase(nameAlias(node.getLocalName()))){
								//一级子节点
								for (int j=0; j<node.getChildNodes().getLength(); j++) {
									Node nodec = node.getChildNodes().item(j);
									//过滤不需要节点
									if (nodec.getLocalName()==null ||
											(exc != null && exc.indexOf(nodec.getNodeName()) != -1) //excluded
											) {
										continue;
									}
									String namec = nameAlias(nodec.getLocalName());
									String nodecId = null;
									if (nodec.getAttributes()!=null && nodec.getAttributes().getNamedItem("id")!=null) {
										nodecId = nodec.getAttributes().getNamedItem("id").getNodeValue();
									}
									if(null!=nodecId&&!"".equals(namec)&&null != namec&&!"incoming".equals(namec)&&!"outgoing".equals(namec)){
										
										String prefixc = prefixAlias(nodec.getPrefix());
										String newNamec = (prefixc != null && prefixc.length()>0) ? (prefixc+":"+namec) : namec;
										DataRow rowc = new DataRow(newNamec);
										if (!nodec.getLocalName().equalsIgnoreCase(namec)) {
											rowc.setFromLocalName(nodec.getLocalName());
										}
										for (int k=0; k<nodec.getAttributes().getLength(); k++) {
											Node attrc = nodec.getAttributes().item(k);
											rowc.addAttribute(attrc.getNodeName(), attrc.getNodeValue());
										}
										
										if ("userTask".equals(namec)) {	//任务
											rowc.addChildren(new DataRow("extensionElements")
													.addChildren(new DataRow("activiti:taskListener")
															.addAttribute("event", "create")
															.addAttribute("class",
																	clsNameBpmTaskListenr)));
											//增加路由信息 default transitioin
											JSONObject elemConf = bpmnConfig.getJSONObject(nodeId);
											if (elemConf.getJSONObject("route") != null) {
												String defaultPath = elemConf.getJSONObject("route").getString("defaultTransition");
												if (defaultPath != null && defaultPath.length()>0) {
													rowc.addAttribute("default", defaultPath);
												}
											}
											checkIsJoinActivity(nodecId);
										} else if ("sequenceFlow".equals(namec)) {	//连线 sequenceFlow
											calculateFlowCondtion(rowc, nodecId);
										} else if ("subProcess".equals(namec)) {
											if ("SubProcessCollapsed".equalsIgnoreCase(bpmnConfig.getJSONObject(nodecId).getString("i18ntype"))) {
												subprocessToCallActivity(rowc, nodecId);
												checkIsJoinActivity(nodecId);
											}
										} else if (namec.indexOf("Gateway")>=0) {
											//增加路由信息 default transitioin
											JSONObject elemConf = bpmnConfig.getJSONObject(nodecId);
											if (elemConf.getJSONObject("route") != null) {
												String defaultPath = elemConf.getJSONObject("route").getString("defaultTransition");
												if (defaultPath != null && defaultPath.length()>0) {
													rowc.addAttribute("default", defaultPath);
												}
											}
										}else if ("serviceTask".equals(ut.getLocalName())) {
											//真实实现使用ReceiveTask
											ut.setLocalName("receiveTask");
											//ut.addAttribute("activiti:class", clsNameServiceTaskDelegate);
											addEventServiceTaskListener(ut, clsNameServiceTaskDelegate);
											//检查是否是合并节点并记录
											checkIsJoinActivity(nodecId);
										}
										ut.addChildren(rowc);
//										ut.removeAttr("incoming", true);
//										ut.removeAttr("outgoing", true);
									}
								}
//							}
							
				        }
					}
					
					//判断多实例循环 Start
					String isMultiple = bpmnConfig.getJSONObject(nodeId).getString("isMultiple");
					if("1".equals(isMultiple)){
						String loopBehaviour = bpmnConfig.getJSONObject(nodeId).getString("loopBehaviour");
						String loopNumberStr = bpmnConfig.getJSONObject(nodeId).getString("loopNumber");
						DataRow milc = new DataRow("multiInstanceLoopCharacteristics");
						milc.addAttribute("isSequential", "parallel".equalsIgnoreCase(loopBehaviour) ? "false" : "true");
//						milc.addElement("isSequential", "parallel".equalsIgnoreCase(loopBehaviour) ? "false" : "true");
						milc.addElement("loopCardinality", loopNumberStr);
						ut.addChildren(milc);
					}
					//判断多实例循环 End
					
					//检查是否是合并节点并记录
					checkIsJoinActivity(nodeId);
				} else if (ut.getLocalName().indexOf("Gateway")>=0) {
					//增加路由信息 default transitioin
					JSONObject elemConf = bpmnConfig.getJSONObject(nodeId);
					if (elemConf.getJSONObject("route") != null) {
						String defaultPath = elemConf.getJSONObject("route").getString("defaultTransition");
						if (defaultPath != null && defaultPath.length()>0) {
							ut.addAttribute("default", defaultPath);
						}
					}
					//互斥和并行不能作为合并
				} else if ("serviceTask".equals(ut.getLocalName())) {
					//真实实现使用ReceiveTask
					ut.setLocalName("receiveTask");
					//ut.addAttribute("activiti:class", clsNameServiceTaskDelegate);
					addEventServiceTaskListener(ut, clsNameServiceTaskDelegate);
					//检查是否是合并节点并记录
					checkIsJoinActivity(nodeId);
				} else if ("startEvent".equals(ut.getLocalName())) {
					//检查是否是触发开始
					JSONObject elemConf = bpmnConfig.getJSONObject(nodeId);
					String messageName = elemConf.getString("messageName");
					if (messageName != null && messageName.length()>0) {
						addMessageEventDefToStartEvent(ut, nodeId, messageName);
					}
				} else if ("receiveTask".equals(ut.getLocalName())) {
					checkIsJoinActivity(nodeId);
				} else if ("intermediateThrowEvent".equals(ut.getLocalName())) {//中间触发事件
					//检查是否是触发开始
					JSONObject elemConf = bpmnConfig.getJSONObject(nodeId);
					String messageName = elemConf.getString("messageName");
					if (messageName != null && messageName.length()>0) {
						addEventDefToIntermediateThrowEvent(ut, nodeId, messageName);
					}
				}
				
			}
			
			//为合并节点添加相容网关和链接线
			/*List<DataRow> newNodes = new ArrayList<DataRow>();
			for (DataRow nol : nodesOnLane) {
				String nolId = nol.getAttribute("id");
				List<String> sourceFlows = joinActivities.get(nolId);
				if (sourceFlows!=null) {//存在即认为是合并节点
					//插入新相容网关
					DataRow ig = new DataRow("inclusiveGateway");
					ig.addAttribute("id", "InclusiveGateway_"+nolId);
					newNodes.add(ig);
					//新相容网关增加一条目标连线到合并节点
					DataRow newSeq = new DataRow("sequenceFlow");
					newSeq.addAttribute("id", "SequenceFlow_IG_"+nolId);
					newSeq.addAttribute("sourceRef", "InclusiveGateway_"+nolId);
					newSeq.addAttribute("targetRef", nolId);
					newNodes.add(newSeq);
				}
				if(null!=nolId && "SubProcess".equalsIgnoreCase(bpmnConfig.getJSONObject(nolId).getString("i18ntype"))){
					List<DataRow> newNodesc = new ArrayList<DataRow>();
					List<DataRow> subprocessChildren = nol.getChildren();
					for(DataRow childno : subprocessChildren){
						String nolcId = childno.getAttribute("id");
						List<String> sourceFlowsc = joinActivities.get(nolcId);
						if (sourceFlowsc!=null) {//存在即认为是合并节点
							//插入新相容网关
							DataRow ig = new DataRow("inclusiveGateway");
							ig.addAttribute("id", "InclusiveGateway_"+nolcId);
							newNodesc.add(ig);
							//新相容网关增加一条目标连线到合并节点
							DataRow newSeq = new DataRow("sequenceFlow");
							newSeq.addAttribute("id", "SequenceFlow_IG_"+nolcId);
							newSeq.addAttribute("sourceRef", "InclusiveGateway_"+nolcId);
							newSeq.addAttribute("targetRef", nolcId);
							newNodesc.add(newSeq);
						}
					}
					subprocessChildren.addAll(newNodesc);
				}
				
				
			}
			if (newNodes.size()>0) {
				nodesOnLane.addAll(newNodes);
			}*/
			List<DataRow> newNodes = getInClusiveGwStaffForJoinActivity (nodesOnLane);
			if (newNodes.size()>0) {
				nodesOnLane.addAll(newNodes);
			}
			
		}
		
		//将messageFlow sequenceFlow统一添加为sequenceFlow到process
		List<DataRow> sequenceFlows = getDataRowByXPath("//bpmn2:messageFlow", false);
		if (sequenceFlows != null && sequenceFlows.size()>0) {
			for (DataRow ut : sequenceFlows) {
				calculateFlowCondtion(ut, ut.getAttribute("id"));
			}
			//changeIdValue(sequenceFlows);
			/*XmlDomUtil.addDataRow(targetDoc, 
					processNode, 
					sequenceFlows);*/
			nodesOnLane.addAll(sequenceFlows);
		}
		
		//在将所有节点增加到目标文档之前，修改合并节点的来源路径，指向到新增加的相容网关
		/*for (Map.Entry<String, List<String>> e : joinActivities.entrySet()) {
			for (String seq : e.getValue()) {
				for (DataRow nol : nodesOnLane) {
					String nolId = nol.getAttribute("id");
					if (seq.equals(nolId)) {
						//修改合并节点的所有来源路径的目标到新的网关
						nol.addAttribute("targetRef", "InclusiveGateway_"+e.getKey());
						break;
					}
					if(null!=nolId && bpmnConfig.containsKey(nolId) && 
							"SubProcess".equalsIgnoreCase(bpmnConfig.getJSONObject(nolId).getString("i18ntype"))){
						List<DataRow> subprocessChildren = nol.getChildren();
						for(DataRow nolc : subprocessChildren){
							String nolcId = nolc.getAttribute("id");
							if(seq.equals(nolcId)){
								nolc.addAttribute("targetRef", "InclusiveGateway_"+e.getKey());
								break;
							}
						}
					}
				}
			}
		}*/
		
		//将nodes加入process
		XmlDomUtil.addDataRow(targetDoc, 
				processNode, 
				nodesOnLane);
		
		//添加顶级节点
		if (firstLvlElems.size()>0) {
			XmlDomUtil.addDataRowToElement(targetDoc, targetDoc.getDocumentElement(), firstLvlElems, true, processNode);
		}
		
		//shapes
		Node planeNode = targetDoc.getElementsByTagName("bpmndi:BPMNPlane").item(0);
		List<DataRow> shapes = getDataRowByXPath("//bpmndi:BPMNDiagram/bpmndi:BPMNPlane/*", true, "bpmndi:BPMNLabel");
		DataRow.removeAttr(shapes, "xsi:type", true);
		DataRow.removeAttr(shapes, "isExpanded", true);
		DataRow.removeAttr(shapes, "isCollapsed", true);
		DataRow.filterAttr(shapes, new String[] { "width", "height", "x", "y" }, new NumberScaleAttrFilter(), true);
		
		//为合并节点添加相容网关的图形
		List<DataRow> newShapes = new ArrayList<DataRow>();
		for (DataRow shape : shapes) {
			String elemId = shape.getAttribute("bpmnElement");
			List<String> sourceFlows = joinActivities.get(elemId);
			if (sourceFlows!=null) {//存在即认为是合并节点
				//获取合并节点图形的信息
				BigDecimal width = new BigDecimal(shape.getChildren().get(0).getAttribute("width"));
				BigDecimal height = new BigDecimal(shape.getChildren().get(0).getAttribute("height"));
				BigDecimal x = new BigDecimal(shape.getChildren().get(0).getAttribute("x"));
				BigDecimal y = new BigDecimal(shape.getChildren().get(0).getAttribute("y"));
				//克隆shape，并赋予新的id，作为新增加的InclusiveGateway图形
				DataRow newShape = new DataRow(shape.getLocalName(), null, null, shape.getChildren());
				newShape.addAttribute("bpmnElement", "InclusiveGateway_"+ elemId);
				newShape.addAttribute("id", "InclusiveGateway_"+ elemId + "_di");
				newShapes.add(newShape);
				//增加一个链接线图形，从新的InclusiveGateway指到合并节点
				DataRow newSeq = new DataRow("bpmndi:BPMNEdge");
				newSeq.addAttribute("id", "SequenceFlow_IG_"+elemId+"_di");
				newSeq.addAttribute("bpmnElement", "SequenceFlow_IG_"+elemId);
				DataRow wp1 = new DataRow("di:waypoint");
				wp1.addAttribute("xsi:type", "dc:Point");
				wp1.addAttribute("x", x.add(width).setScale(1).toString());
				wp1.addAttribute("y", y.add(height.divide(new BigDecimal(2))).setScale(1).toString());
				DataRow wp2 = new DataRow("di:waypoint");
				wp2.addAttribute("xsi:type", "dc:Point");
				wp2.addAttribute("x", x.setScale(1).toString());
				wp2.addAttribute("y", y.add(height.divide(new BigDecimal(2))).setScale(1).toString());
				newSeq.addChildren(wp1, wp2);
				newShapes.add(newSeq);
			}
		}
		if (newShapes.size()>0) {
			shapes.addAll(newShapes);
		}
		
		//添加图形
		XmlDomUtil.addDataRow(targetDoc, 
				planeNode, 
				shapes);
		/*
		 * add a shape for pool1
		 *  <bpmndi:BPMNShape bpmnElement="pool1" id="BPMNShape_pool1">
         *     <omgdc:Bounds height="所有Participant height和" width="所有Participant width最大值" x="0.0" y="0.0"></omgdc:Bounds>
         *  </bpmndi:BPMNShape>
		 */
		
	}
    
    /**
     * add event definition for start event
     * @param ut
     * @param nodeId
     */
    private void addMessageEventDefToStartEvent(DataRow ut, String nodeId, String name) {
    	String msgId = this.processId+"_"+this.deployingVersion+"_"+nodeId;
    	DataRow sed = new DataRow("messageEventDefinition")
    		.addAttribute("id", nodeId+"_EventDefinition")
    		.addAttribute("messageRef", msgId);
    	ut.addChildren(sed);
    	//add global message name to process
    	firstLvlElems.add(new DataRow("message").addAttribute("id", msgId).addAttribute("name", msgId));//通过message name触发
    }
    
    /**
     * add event definition
     * @param ut
     * @param nodeId
     */
    private void addEventDefToIntermediateThrowEvent(DataRow ut, String nodeId, String name) {
    	/*DataRow executionListener = new DataRow("activiti:executionListener")
    		.addAttribute("class", "com.sino.bpm.engine.common.CommonEventListener")
    		.addAttribute("event", "start");
    	DataRow extensionElements = new DataRow("extensionElements")
    		.addChildren(executionListener);
    	ut.addChildren(extensionElements);*/
    }

	/**
	 * add service task event listener (Use Receivetask)
	 * @param ut
	 * @param className
	 */
	private void addEventServiceTaskListener(DataRow ut, String className) {
    	DataRow executionListenerStart = new DataRow("activiti:executionListener")
    		.addAttribute("event", "start")
    		.addAttribute("class", className);
		DataRow executionListenerEnd = new DataRow("activiti:executionListener")
				.addAttribute("event", "end")
				.addAttribute("class", className);
    	DataRow extensionElements = new DataRow("extensionElements")
    		.addChildren(executionListenerStart).addChildren(executionListenerEnd);
    	ut.addChildren(extensionElements);
	}
    
    /**
     * change subprocess collapsed to call activity
     * @param ut
     * @param nodeId
     */
    private void subprocessToCallActivity(DataRow ut, String nodeId) {
    	ut.setLocalName("callActivity");
		ut.addAttribute("calledElement", bpmnConfig.getJSONObject(nodeId).getJSONObject("processRef").getString("id"));
		//是否异步
		if ("1".equals(bpmnConfig.getJSONObject(nodeId).getString("async"))) {
			ut.addAttribute("activiti:async", "true");
		}
		DataRow extensionElements = new DataRow("extensionElements");
		String[][] ins = new String[][]{
				//source, sourceExpression, target, targetExpression
				new String[]{null, "true", EngineConst.PV_IS_CALLACTIVITY, null},
				new String[]{null, nodeId, EngineConst.PV_CALLACTIVITY_ID, null},
//				new String[]{null, "${processVar.bpdId}", "parentBpdId", null},
//				new String[]{null, "${processVar.bizId}", "parentBizId", null},
				new String[]{null, "${"+ EngineConst.PV_MAINPIID+"}", EngineConst.PV_MAINPIID, null},
//				new String[]{"piid", null, "parentPiid", null},
				new String[]{null, "${"+ EngineConst.PV_PIEID +"}", EngineConst.PV_PARENT_PIEID, null},
				new String[]{null, "${"+ EngineConst.PV_SKIP_TO +"}", EngineConst.PV_SKIP_TO, null}
				};
		for (String[] in : ins) {
			DataRow inrow = new DataRow("activiti:in");
			if (in[0] != null) {
				inrow.addAttribute("source", in[0]);
			}
			if (in[1] != null) {
				inrow.addAttribute("sourceExpression", in[1]);
			}
			if (in[2] != null) {
				inrow.addAttribute("target", in[2]);
			}
			if (in[3] != null) {
				inrow.addAttribute("targetExpression", in[3]);
			}
			extensionElements.addChildren(inrow);
		}
		//查找映射
		/*JSONObject dataMapping = bpmnConfig.getJSONObject(nodeId).getJSONObject("dataMapping");
		if (dataMapping!=null && !dataMapping.isEmpty()) {
			JSONObject input = dataMapping.getJSONObject("input");
			if (input!=null && !input.isEmpty()) {
				for (String key : input.keySet()) {
					DataRow inrow = new DataRow("activiti:in");
					inrow.addAttribute("sourceExpression", "${processVar.variables."+input.getString(key)+".value}");
					inrow.addAttribute("target", key);
					extensionElements.addChildren(inrow);
				}
			}
		}*/
		ut.setChildren(null);
		ut.addChildren(extensionElements);
    }
    
    /**
     * @MethodName: isJoinActivity
     * @Description: 如果不是网关，并且作为分支合并节点，则在该节点之前增加一个相容网关，网关id为 [InclusiveGateway_ + 节点id]
     * @param nodeId
     * @throws XPathExpressionException
     * @throws IOException
     */
    private void checkIsJoinActivity(String nodeId) throws XPathExpressionException, IOException {
    	List<DataRow> incomeMs = getDataRowByXPath("//bpmn2:messageFlow[@targetRef='"+nodeId+"']", false);
		List<DataRow> incomeSs = getDataRowByXPath("//bpmn2:sequenceFlow[@targetRef='"+nodeId+"']", false);
		if (incomeMs.size() + incomeSs.size() > 1) {
			logger.finer("found join activity:" + nodeId);
			List<String> sourceFLows = joinActivities.get(nodeId);
			if (sourceFLows==null) {
				sourceFLows = new ArrayList<String>();
			}
			for (DataRow row : incomeMs) {
				sourceFLows.add(row.getAttribute("id"));
			}
			for (DataRow row : incomeSs) {
				sourceFLows.add(row.getAttribute("id"));
			}
			joinActivities.put(nodeId, sourceFLows);
		}
    }
    
    /**
     * 迭代需要增加相容网关的组件，包括子流程中的
     * @param nodes
     */
    private List<DataRow> getInClusiveGwStaffForJoinActivity (List<DataRow> nodes) {
    	List<DataRow> newNodes = new ArrayList<DataRow>();
		for (DataRow nol : nodes) {
			String nolId = nol.getAttribute("id");
			if (nolId==null || !bpmnConfig.containsKey(nolId)) {
				continue;
			}
			List<String> sourceFlows = joinActivities.get(nolId);
			if (sourceFlows!=null) {//存在即认为是合并节点
				//插入新相容网关
				DataRow ig = new DataRow("inclusiveGateway");
				ig.addAttribute("id", "InclusiveGateway_"+nolId);
				newNodes.add(ig);
				//新相容网关增加一条目标连线到合并节点
				DataRow newSeq = new DataRow("sequenceFlow");
				newSeq.addAttribute("id", "SequenceFlow_IG_"+nolId);
				newSeq.addAttribute("sourceRef", "InclusiveGateway_"+nolId);
				newSeq.addAttribute("targetRef", nolId);
				newNodes.add(newSeq);
				//查找所有指向该合并节点的线，指向到新的相容网关
				for (String sourceSeq : sourceFlows) {
					for (DataRow seqnode : nodes) {
						if (seqnode.getLocalName().equals("sequenceFlow")
								&& sourceSeq.equals(seqnode.getAttribute("id"))) {
							seqnode.addAttribute("targetRef", "InclusiveGateway_"+nolId);
						}
					}
				}
			}
			if("SubProcess".equalsIgnoreCase(bpmnConfig.getJSONObject(nolId).getString("i18ntype"))
					&& nol.getChildren() != null && nol.getChildren().size()>0){//一般内嵌子流程
				List<DataRow> newChildren = getInClusiveGwStaffForJoinActivity (nol.getChildren());
				if (newChildren.size()>0) {
					nol.getChildren().addAll(newChildren);
				}
			}
		}
		return newNodes;
    }
	
    /**
     * @MethodName: getDataRowByXPath
     * @Description: 根据XPath获取元素，返回类型为DataRow
     * @param expression xpath表达式
     * @param withChilldren 包含子节点
     * @param excludedChildren 不包含的节点
     * @return
     * @throws XPathExpressionException
     * @throws IOException List<DataRow>
     */
	private List<DataRow> getDataRowByXPath(String expression, boolean withChilldren, String... excludedChildren) throws XPathExpressionException, IOException {

		NodeList nl = getNodesByXPath(expression);
		List<DataRow> rtn = new ArrayList<DataRow>();
		List<String> exc = Arrays.asList(excludedChildren);
		
		for (int i=0; i<nl.getLength(); i++) {
			Node node = nl.item(i);
			
			if (node.getLocalName()==null ||
					(exc != null && exc.indexOf(node.getNodeName()) != -1) //excluded
					) {
				continue;
			}
			
			String name = nameAlias(node.getLocalName());
			String prefix = prefixAlias(node.getPrefix());
			String newName = (prefix != null && prefix.length()>0) ? (prefix+":"+name) : name;
			
			DataRow row = new DataRow(newName);
			if (!node.getLocalName().equalsIgnoreCase(name)) {
				row.setFromLocalName(node.getLocalName());
			}
			for (int j=0; j<node.getAttributes().getLength(); j++) {
				Node attr = node.getAttributes().item(j);
				row.addAttribute(attr.getNodeName(), attr.getNodeValue());
			}
			if (withChilldren) {
				//一级子节点
				for (int j=0; j<node.getChildNodes().getLength(); j++) {
					Node nodec = node.getChildNodes().item(j);
					if (nodec.getLocalName()==null ||
							(exc != null && exc.indexOf(nodec.getNodeName()) != -1) //excluded
							) {
						continue;
					}
					String namec = nameAlias(nodec.getLocalName());
					String prefixc = prefixAlias(nodec.getPrefix());
					String newNamec = (prefixc != null && prefixc.length()>0) ? (prefixc+":"+namec) : namec;
					DataRow rowc = new DataRow(newNamec);
					if (!nodec.getLocalName().equalsIgnoreCase(namec)) {
						rowc.setFromLocalName(nodec.getLocalName());
					}
					for (int k=0; k<nodec.getAttributes().getLength(); k++) {
						Node attrc = nodec.getAttributes().item(k);
						rowc.addAttribute(attrc.getNodeName(), attrc.getNodeValue());
					}
					//textContent
					if (nodec.getTextContent()!=null) {
						rowc.setTextContent(nodec.getTextContent());
					} else {
						//二级子节点
						for (int l=0; l<nodec.getChildNodes().getLength(); l++) {
							Node nodecc = node.getChildNodes().item(l);
							if (nodecc.getLocalName()==null ||
									(exc != null && exc.indexOf(nodecc.getNodeName()) != -1) //excluded
									) {
								continue;
							}
							String namecc = nameAlias(nodecc.getLocalName());
							String prefixcc = prefixAlias(nodecc.getPrefix());
							String newNamecc = (prefixcc != null && prefixcc.length()>0) ? (prefixcc+":"+namecc) : namecc;
							DataRow rowcc = new DataRow(newNamecc);
							if (!nodecc.getLocalName().equalsIgnoreCase(namecc)) {
								rowcc.setFromLocalName(nodecc.getLocalName());
							}
							for (int k=0; k<nodecc.getAttributes().getLength(); k++) {
								Node attrcc = nodecc.getAttributes().item(k);
								rowcc.addAttribute(attrcc.getNodeName(), attrcc.getNodeValue());
							}
							rowc.addChildren(rowcc);
						}
					}
					row.addChildren(rowc);
				}
			}
			//DataRow.print(row);
			rtn.add(row);
        }
        
        return rtn;
	}
	
	private NodeList getNodesByXPath(String expression) throws XPathExpressionException, IOException {
		XPathFactory f = XPathFactory.newInstance();
		XPath xpath = f.newXPath();
		xpath.setNamespaceContext(new BpmnNamespaceContext());
		/*//过滤不需要的节点
		StringBuilder newexp = new StringBuilder();
		newexp.append(expression);
		for (String omitnode : this.omitNodes) {
			newexp.append("[name(.) != '"+omitnode+"']");
		}*/
        XPathExpression ex = xpath.compile(expression);
        NodeList nl = (NodeList) ex.evaluate(new InputSource(new StringReader(sourceXmlStr)), XPathConstants.NODESET);
        
        /*logger.finer(nl.getLength());
        for (int i=0; i<nl.getLength(); i++) {
        	logger.finer("==========================================");
        	logger.finer("="+nl.item(i).getLocalName());
            logger.finer("="+nl.item(i).getBaseURI());
            logger.finer("="+nl.item(i).getNamespaceURI());
            logger.finer("="+nl.item(i).getNodeName());
            logger.finer("="+nl.item(i).getNodeType());
            logger.finer("="+nl.item(i).getNodeValue());
            logger.finer("="+nl.item(i).getPrefix());
            logger.finer("="+nl.item(i).getTextContent());
            Node node = nl.item(i);
        }*/
        
        return nl;
	}
	
	private void initAlias() {
		//prefix alias
		prefixAlias = new HashMap<String, String>();
		prefixAlias.put("bpmn2", XMLConstants.DEFAULT_NS_PREFIX);
		//nameAlias.put("dc", "omgdc");
		prefixAlias.put("di", "omgdi");
		
		//name alias
		nameAlias = new HashMap<String, String>();
		nameAlias.put("messageFlow", "sequenceFlow");
		nameAlias.put("process", "lane");
		nameAlias.put("participant", "lane");
	}
	
	private String prefixAlias(String nsPrefix) {
		String alias = prefixAlias.get(nsPrefix);
		if (alias==null) {
			return nsPrefix;
		} else {
			return alias;
		}
	}
	
	private String nameAlias(String name) {
		String alias = nameAlias.get(name);
		if (alias==null) {
			return name;
		} else {
			return alias;
		}
	}
	
	private void calculateFlowCondtion(DataRow ut, String nodeId) throws XPathExpressionException, IOException {
		//获得来源节点设置
		String sourceNodeId = ut.getAttribute("sourceRef");
		logger.finest("nodesOnLane === "+ut.getLocalName()+"["+nodeId+"] sourceTaskConf="+sourceNodeId);
		//获取来源节点的outgoings
		List<DataRow> outgoings = getDataRowByXPath("//*[@id='"+sourceNodeId+"']/bpmn2:outgoing", true);
		if (outgoings!=null && outgoings.size()>1) {
			String defaultPath = null;
			JSONObject routeConf = bpmnConfig.getJSONObject(sourceNodeId).getJSONObject("route");
			if (routeConf!=null) {
				defaultPath = routeConf.getString("defaultTransition");
			}
			if (!nodeId.equalsIgnoreCase(defaultPath)) {
				DataRow cond = new DataRow("conditionExpression");
				cond.addAttribute("xsi:type", "tFormalExpression");
				cond.setUseCDATAForTextContent(true);
				cond.setTextContent("${processVar.gatewayVar."+nodeId+"=='true'||processVar.gatewayVar."+nodeId+"==null}");
				ut.addChildren(cond);
			}
		}
		
		/*JSONObject sourceConf = bpmnConfig.getJSONObject(sourceNodeId);
		//获得来源节点路由设置
		JSONObject routeConf = sourceConf.getJSONObject("route");
		if (routeConf != null) {
			//获得条件设置
			JSONObject transConds = routeConf.getJSONObject("transitionConditions");
			if (null != transConds) {
				//获取所有条件
				JSONArray conds = transConds.getJSONArray(nodeId);
				if (null != conds && conds.size()>0) {
					DataRow cond = new DataRow("conditionExpression");
					cond.addAttribute("xsi:type", "tFormalExpression");
					cond.setUseCDATAForTextContent(true);
					cond.setTextContent("${processVar.gatewayVar."+nodeId+"=='true'}");
					ut.addChildren(cond);
				}
			}
		}*/
	}
	
	/*private void changeIdValue(DataRow datarow) {
		if (datarow.getAttributes().containsKey("id")) {
			String v = datarow.getAttributes().get("id");
			if (v != null && datarow.getFromLocalName() != null && v.toLowerCase().startsWith(datarow.getFromLocalName().toLowerCase()+"_")) {
				datarow.getAttributes().put("id", datarow.getLocalName().toLowerCase()+
						"_"+v.substring(datarow.getFromLocalName().toLowerCase().length()+1));
			}
		}
	}
	
	private void changeIdValue(List<DataRow> datarows) {
		for (DataRow datarow : datarows) {
			if (datarow.getAttributes().containsKey("id")) {
				String v = datarow.getAttributes().get("id");
				if (v != null && datarow.getFromLocalName() != null && v.toLowerCase().startsWith(datarow.getFromLocalName().toLowerCase()+"_")) {
					datarow.getAttributes().put("id", datarow.getLocalName().toLowerCase()+
							"_"+v.substring(datarow.getFromLocalName().toLowerCase().length()+1));
				}
			}
		}
	}*/
	
	
	/*public static void main(String[] args) throws Exception {
		String sourceXmlstr = FileUtils.readFileToString(new File("F:/1316(6).bpmn"), Const.ENCODING);
		//logger.finer(xmlstr);
		BpmnRender render = new BpmnRender("myprocessid1", "My process 1", 0, sourceXmlstr, null, null);
		logger.finer(render.getTargetDocumentString());
		render.toFile("F:/1.bpmn");
	}*/
}
